import { createAction, createReducer, ActionType, createAsyncAction } from 'typesafe-actions';
import { Dispatch } from 'redux';
import Axios from 'axios';
import _ from 'lodash';

export interface WordTagIndexDto {
    wordTagValue: string;
}

export interface WordTagDto {
    dtos: WordTagIndexDto[]
}

export interface WordTagState {
    dto: WordTagDto
}

const setState = createAction('WORD_TAG/SET_STATE')<WordTagState>();
export const addTag = createAction('WORD_TAG/ADD_TAG')<string>();

const fetchActions = createAsyncAction(
    'WORD_TAG/FETCH/REQUEST',
    ['WORD_TAG/FETCH/SUCCESS', (res: WordTagDto) => res],
    ['WORD_TAG/FETCH/FAILURE', (err: Error) => err],
    'WORD_TAG/FETCH/CANCEL',
)();

const addActions = createAsyncAction(
    'WORD_TAG/ADD/REQUEST',
    ['WORD_TAG/ADD/SUCCESS', (res: string) => res],
    ['WORD_TAG/ADD/FAILURE', (err: Error) => err],
    'WORD_TAG/ADD/CANCEL',
)();
const editActions = createAsyncAction(
    'WORD_TAG/EDIT/REQUEST',
    ['WORD_TAG/EDIT/SUCCESS', (res: { oldValue: string, newValue: string }) => res],
    ['WORD_TAG/EDIT/FAILURE', (err: Error) => err],
    'WORD_TAG/EDIT/CANCEL',
)();
const removeActions = createAsyncAction(
    'WORD_TAG/REMOVE/REQUEST',
    ['WORD_TAG/REMOVE/SUCCESS', (res: string) => res],
    ['WORD_TAG/REMOVE/FAILURE', (err: Error) => err],
    'WORD_TAG/REMOVE/CANCEL',
)();

export type Actions = ActionType<typeof setState>
    | ActionType<typeof fetchActions>
    | ActionType<typeof addActions>
    | ActionType<typeof editActions>
    | ActionType<typeof removeActions>
    | ActionType<typeof addTag>
    ;

export const fetch = (wordValue: string) =>
    async (dispatch: Dispatch) =>
        Axios.get<WordTagDto>(`wordBase/tag/${wordValue}`)
            .then(res => dispatch(fetchActions.success(res.data)))
            .catch(error => dispatch(fetchActions.failure(error)))

export const add = (wordValue: string, wordTagValue: string) =>
    async (dispatch: Dispatch) =>
        Axios.post(`wordBase/${wordValue}/${wordTagValue}`)
            .then(_ => dispatch(addActions.success(wordTagValue)))
            .catch(err => dispatch(addActions.failure(err)))

export const edit = (wordValue: string, oldValue: string, newValue: string) =>
    async (dispatch: Dispatch) =>
        Axios.put(`wordBase/tag/${wordValue}/${oldValue}/${newValue}`)
            .then(_ => dispatch(editActions.success({ oldValue, newValue })))
            .catch(err => dispatch(editActions.failure(err)))

export const remove = (wordTagValue: string, oldValue: string) =>
    async (dispatch: Dispatch) =>
        Axios.delete(`wordBase/tag/${wordTagValue}/${oldValue}`)
            .then(_ => dispatch(removeActions.success(oldValue)))
            .catch(err => dispatch(removeActions.failure(err)))

const initialState: WordTagState = {
    dto: { dtos: [] }
};

export const Reducer = createReducer<WordTagState, Actions>(initialState)
    .handleAction(setState, (_, action) => action.payload)
    .handleAction(fetchActions.success, (state, action) => {
        const newState = _.cloneDeep(state)
        newState.dto = action.payload
        return newState
    })
    .handleAction(addActions.success, (state, action) => {
        const newState = _.cloneDeep(state)
        newState.dto.dtos.push({ wordTagValue: action.payload })
        return newState
    })
    .handleAction(editActions.success, (state, action) => {
        const newState = _.cloneDeep(state)
        const i = newState.dto.dtos.findIndex(e => e.wordTagValue === action.payload.oldValue)
        newState.dto.dtos[i].wordTagValue = action.payload.newValue
        return newState
    })
    .handleAction(removeActions.success, (state, action) => {
        const newState = _.cloneDeep(state)
        const r = newState.dto.dtos.filter(e => e.wordTagValue !== action.payload)
        newState.dto.dtos = r
        return newState
    })
    .handleAction(addTag, (state, action) => {
        const newState = _.cloneDeep(state)
        const i = state.dto.dtos.findIndex(e => e.wordTagValue === action.payload)
        if (i === -1) {
            newState.dto.dtos.push({ wordTagValue: action.payload })
        }
        return newState
    })